package cn.plem.ml;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.TermCriteria;
import org.opencv.ml.ANN_MLP;
import org.opencv.ml.TrainData;

public class Ann {

	static {
		nu.pattern.OpenCV.loadShared();
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	/**
	 * ml.properties
	  
	    # cv.ml.ANN_MLP.TrainingMethods 
		# BACKPROP = 0,RPROP = 1,ANNEAL = 2
		cv.ml.ANN_MLP.TrainingMethods=0
		
		# cv.ml.ANN_MLP.ActivationFunctions 
		# IDENTITY = 0,SIGMOID_SYM = 1,GAUSSIAN = 2,RELU = 3,LEAKYRELU = 4 
		cv.ml.ANN_MLP.ActivationFunctions=1
		
		layer.hidden=32,16
		
		termCriteria.maxCount=500
		termCriteria.epsilon=1e-5
	 */
	ResourceBundle rb = ResourceBundle.getBundle("ml");

	ANN_MLP ann;
	TrainData trainData;

	public static void main(String[] args) throws IOException {
		CsvTrain csvTrain = new CsvTrain("iris");
		TrainData trainData = csvTrain.trainData(Paths.get("iris.data"), CvType.CV_32FC1);
		Path xml = csvTrain.path("iris.ann.xml");
		Ann ann = new Ann(trainData, xml);
		float[] d = { 5.7f, 2.8f, 4.1f, 1.3f };
		//float[] d = { 5.9f, 3.0f, 5.1f, 1.8f };
		//5.9,3.0,5.1,1.8,Iris-virginica
		System.out.println("predict:" + ann.predict(d));
	}

	public Ann(TrainData trainData, Path xml) throws IOException {
		this.trainData = trainData;
		if (xml.toFile().exists()) {
			ann = ANN_MLP.load(xml.toString());
		} else {
			ann = ANN_MLP.create();
			train(trainData);
			ann.save(xml.toString());
		}
	}

	public boolean train(TrainData trainData) {
		int trainMethod = rb.containsKey("cv.ml.ANN_MLP.TrainingMethods")
				? Integer.parseInt(rb.getString("cv.ml.ANN_MLP.TrainingMethods"))
				: ANN_MLP.BACKPROP;
		int activationFunction = rb.containsKey("cv.ml.ANN_MLP.ActivationFunctions")
				? Integer.parseInt(rb.getString("cv.ml.ANN_MLP.ActivationFunctions"))
				: ANN_MLP.SIGMOID_SYM;
		int maxCount = rb.containsKey("termCriteria.maxCount") ? Integer.parseInt(rb.getString("termCriteria.maxCount"))
				: 100;
		double epsilon = rb.containsKey("termCriteria.epsilon")
				? Double.parseDouble(rb.getString("termCriteria.epsilon"))
				: 1e-7;

		String hidden = rb.containsKey("layer.hidden") ? rb.getString("layer.hidden") : "10";
		String[] h = hidden.split(",");
		Mat layerMat = new Mat(h.length + 2, 1, CvType.CV_32SC1);
		layerMat.put(0, 0,  trainData.getSamples().cols());//
		layerMat.put(h.length + 1, 0, trainData.getResponses().cols()); 
		for (int i = 1; i <= h.length; i++) {
			layerMat.put(i, 0, Integer.parseInt(h[i - 1]));
		}
		ann.setLayerSizes(layerMat);

		ann.setTrainMethod(trainMethod);
		ann.setActivationFunction(activationFunction);
		ann.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER + TermCriteria.EPS, maxCount, epsilon));

		return ann.train(trainData);
	}

	/**
	 * 
	 * @param data = { 5.7f, 2.8f, 4.1f, 1.3f };
	 * @return
	 */
	public int predict(float[] data) {
		int cols = trainData.getSamples().cols();
		Mat sampleMat = new Mat(1, cols, CvType.CV_32FC1);
		sampleMat.put(0, 0, data);
		Mat resultMat = new Mat();
		ann.predict(sampleMat,resultMat);
		System.out.println(resultMat.dump() );
		return (int)Math.round(resultMat.get(0,0)[0]);
	}

}
